package com.autumn.security.configure;

import java.util.ArrayList;
import java.util.List;

import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.session.mgt.eis.SessionDAO;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

import com.autumn.security.auth.credential.AutumnCredentialsMatcher;
import com.autumn.security.crypto.AutumnMd5PasswordProvider;
import com.autumn.security.crypto.IAutumnPasswordProvider;
import com.autumn.security.filter.exception.AuthenticationExceptionFilter;
import com.autumn.security.realm.AutumnAuthorizingRealm;
import com.autumn.security.user.IAutumnUserCredentialsService;
import com.autumn.spring.boot.properties.AutumnAuthProperties;

/**
 * 
 * 安全配置
 * 
 * @author 杨昌国 2018-12-06 14:01:54
 */
@Configuration
@EnableConfigurationProperties({ AutumnAuthProperties.class })
public class AutumnSecurityConfiguration {

	/**
	 * 权限异常过滤
	 * 
	 * @return
	 */
	@Bean
	@ConditionalOnMissingBean(AuthenticationExceptionFilter.class)
	public AuthenticationExceptionFilter authenticationExceptionFilter() {
		return new AuthenticationExceptionFilter();
	}

	/**
	 * 密码驱动
	 * 
	 * @return
	 */
	@Bean
	@ConditionalOnMissingBean(IAutumnPasswordProvider.class)
	public IAutumnPasswordProvider autumnPasswordProvider() {
		return new AutumnMd5PasswordProvider();
	}

	/**
	 * Realm
	 * 
	 * @param passwordProvider
	 * @param credentialsService
	 * @return
	 */
	@Bean
	@ConditionalOnMissingBean(AutumnAuthorizingRealm.class)
	public AutumnAuthorizingRealm autumnAuthorizingRealm(IAutumnPasswordProvider passwordProvider,
			IAutumnUserCredentialsService credentialsService) {
		AutumnAuthorizingRealm realm = new AutumnAuthorizingRealm(passwordProvider, credentialsService);
		realm.setCredentialsMatcher(this.autumnCredentialsMatcher(credentialsService));
		return realm;
	}

	/**
	 * 密码比较器
	 * 
	 * @param userService 用户服务
	 * @return
	 */
	@Bean
	@ConditionalOnMissingBean(AutumnCredentialsMatcher.class)
	public AutumnCredentialsMatcher autumnCredentialsMatcher(IAutumnUserCredentialsService credentialsService) {
		return new AutumnCredentialsMatcher(this.autumnPasswordProvider(), credentialsService);
	}

	/**
	 * 会话管理
	 * 
	 * @param shiroSessionDAO
	 * @return
	 */
	@Bean
	@ConditionalOnMissingBean(SessionManager.class)
	public SessionManager shiroSessionManager(SessionDAO shiroSessionDAO) {
		DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
		sessionManager.setSessionDAO(shiroSessionDAO);
		return sessionManager;
	}

	/**
	 * 
	 * @param authProperties
	 * @param cacheManager
	 * @param sessionManager
	 * @return
	 */
	@Bean
	public org.apache.shiro.mgt.SecurityManager securityManager(AutumnAuthProperties authProperties,
			CacheManager cacheManager, SessionManager sessionManager, IAutumnPasswordProvider passwordProvider,
			IAutumnUserCredentialsService userService) {
		DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();

		// 设置realm.
		// securityManager.setAuthenticator(modularRealmAuthenticator());

		List<Realm> realms = new ArrayList<>();
		realms.add(this.autumnAuthorizingRealm(passwordProvider, userService));
		securityManager.setRealms(realms);

		// HashedCredentialsMatcher aa;

		securityManager.setCacheManager(cacheManager);
		securityManager.setSessionManager(sessionManager);
		// securityManager.setSubjectFactory(subjectFactory);

		// 注入记住我管理器;
		// securityManager.setRememberMeManager(rememberMeManager());
		return securityManager;
	}

	/**
	 * 
	 * @param securityManager
	 * @return
	 */
	@Bean
	public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(
			org.apache.shiro.mgt.SecurityManager securityManager) {
		AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
		authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
		return authorizationAttributeSourceAdvisor;
	}

	/**
	 * 此注解解决能过注解授权
	 * 
	 * @return
	 */
	@Bean
	@ConditionalOnMissingBean
	public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
		DefaultAdvisorAutoProxyCreator app = new DefaultAdvisorAutoProxyCreator();
		app.setProxyTargetClass(true);
		return app;
	}

	@Bean
	public ShiroFilterFactoryBean shiroFilterFactoryBean(org.apache.shiro.mgt.SecurityManager securityManager,
			IAutumnPermissionFilter autumnPermissionFilter) {
		ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();

		factoryBean.setSecurityManager(securityManager);

		// Map<String, Filter> filters = factoryBean.getFilters();

		// filters.put("authc", new AutumnShiroUserFilter());

		// filters.put("perms", new AutumnPermissionsAuthorizationFilter());

		autumnPermissionFilter.initFilter(factoryBean, securityManager);
		return factoryBean;
	}

	@Bean
	public FilterRegistrationBean filterRegistrationBean() {
		CorsConfiguration corsConfiguration = new CorsConfiguration();
		corsConfiguration.setAllowCredentials(true);
		corsConfiguration.addAllowedOrigin("*");
		corsConfiguration.addAllowedHeader("*");
		corsConfiguration.addAllowedMethod("*");
		
		UrlBasedCorsConfigurationSource configurationSource = new UrlBasedCorsConfigurationSource();
		configurationSource.registerCorsConfiguration("/**", corsConfiguration);
		CorsFilter corsFilter = new CorsFilter(configurationSource);
		FilterRegistrationBean bean = new FilterRegistrationBean(corsFilter);
		bean.setOrder(0);
		return bean;
	}

}
